import React, { useState, useEffect } from 'react';
import {
  TextField,
  List,
  ListItem,
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Typography,
  InputAdornment,
  IconButton,
  Box,
} from '@mui/material';
import { ExpandMore as ExpandMoreIcon, Folder as FolderIcon } from '@mui/icons-material';
import FileBrowser from '../../general/FileBrowser';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import ActionValidationDialog from './ActionValidationDialog';
import Tooltip from '../../general/Tooltip';
import Checkbox from '../../general/Checkbox';
import { NETPYNE_COMMANDS } from '../../../constants';
import { useAppSelector, useAppDispatch } from '../../../redux/hooks';
import { registerModelPath } from '../../../redux/slices/generalSlice';
import { evalPythonMessage } from '../../general/GeppettoJupyterUtils';

const saveOptions = [
  {
    label: 'Export Network Parameters as Python',
    label2: '',
    state: 'exportNetParamsAsPython',
  },
  {
    label: 'Export Simulation Configuration as Python',
    label2: '',
    state: 'exportSimConfigAsPython',
  },
];

const DEFAULT_DIR = 'saved_models';

interface OverwriteModelDialogProps {
  open?: boolean;
  onRequestClose?: () => void;
  [key: string]: any;
}

/**
 * OverwriteModelDialog Component
 * Dialog for saving model to index file with overwrite protection
 */
const OverwriteModelDialog: React.FC<OverwriteModelDialogProps> = ({
  open = false,
  onRequestClose,
  ...otherProps
}) => {
  const dispatch = useAppDispatch();
  const srcPath = useAppSelector((state) => state.general.modelPath);

  const [explorerDialogOpen, setExplorerDialogOpen] = useState(false);
  const [openOverwriteDialog, setOpenOverwriteDialog] = useState(false);
  const [isDirectoryValid, setIsDirectoryValid] = useState(false);
  const [explorerParameter, setExplorerParameter] = useState('srcPath');
  const [dstPath, setDstPath] = useState(srcPath || DEFAULT_DIR);
  const [options, setOptions] = useState({
    exportNetParamsAsPython: false,
    exportSimConfigAsPython: false,
  });

  useEffect(() => {
    // Cleanup on close
    return () => {
      if (onRequestClose) {
        onRequestClose();
      }
    };
  }, []);

  useEffect(() => {
    updateFullPath(dstPath);
    // Check if directory is valid
    setIsDirectoryValid(dstPath !== '');
  }, [dstPath]);

  const updateFullPath = (value: string): void => {
    if (value && value.startsWith('/')) {
      // We know we will be in a UNIX like env.
      return;
    }

    const actualValue = !value ? DEFAULT_DIR : value;

    evalPythonMessage('netpyne_geppetto.getFullPath', [null, actualValue])
      .then((fullpath: string) => {
        setDstPath(`${fullpath}/`);
      })
      .catch(() => {
        // Error handling
      });
  };

  const getDirAndModuleFromPath = (
    fullpath: string,
  ): { dirPath: string; moduleName: string } => {
    const fileName = fullpath.replace(/^.*[\\/]/, '');
    const moduleName = fileName.replace(/\.[^/.]+$/, '');
    const dirPath = fullpath.split(fileName).slice(0, -1).join('');

    return {
      dirPath,
      moduleName,
    };
  };

  const registerSavedModelPath = (path: string): void => {
    dispatch(registerModelPath(path));
  };

  const showExplorerDialog = (param: string): void => {
    setExplorerDialogOpen(true);
    setExplorerParameter(param);
  };

  const closeExplorerDialog = (fieldValue?: any): void => {
    setExplorerDialogOpen(false);
    if (fieldValue) {
      const { dirPath, moduleName } = getDirAndModuleFromPath(fieldValue.path);

      if (explorerParameter === 'dstPath') {
        setDstPath(`${dirPath}${moduleName}`);
      }
    }
  };

  const switchCheckBox = (state: string): void => {
    setOptions((prevOptions) => ({
      ...prevOptions,
      [state]: !prevOptions[state as keyof typeof prevOptions],
    }));
  };

  const saveModel = (): void => {
    const args = [
      srcPath,
      dstPath,
      options.exportNetParamsAsPython,
      options.exportSimConfigAsPython,
    ];
    evalPythonMessage(NETPYNE_COMMANDS.saveModel, args)
      .then(() => {
        registerSavedModelPath(dstPath);
        if (onRequestClose) {
          onRequestClose();
        }
      })
      .catch((error) => {
        console.error('Error saving model:', error);
      });
  };

  const checkDirExistence = async (command: string, path: string): Promise<void> => {
    // If srcPath === dstPath, then a first save have been made to this directory.
    // We can conclude that this save is to overwrite the current model, no need to ask for confirmation.
    if (srcPath === dstPath) {
      saveModel();
      return;
    }
    const exists = await evalPythonMessage(command, [path]);
    // If the path exists, we ask for confirmation
    if (exists) {
      setOpenOverwriteDialog(true);
      return;
    }
    // Otherwise, we save
    saveModel();
  };

  return (
    <>
      <ActionValidationDialog
        command="netpyne_geppetto.checkFileExists"
        callback={(cmd: string, args: any) => checkDirExistence(cmd, args)}
        message="EXPORTING_MODEL"
        buttonLabel="Save"
        title="Save as JSON file"
        args={{ dstPath }}
        disabledButton={!isDirectoryValid}
        open={open}
        onRequestClose={onRequestClose}
        {...otherProps}
      >
        <TextField
          error={!isDirectoryValid}
          helperText={!isDirectoryValid ? 'Please select a directory' : ''}
          variant="filled"
          fullWidth
          value={dstPath}
          onChange={(e) => setDstPath(e.target.value)}
          label="Model destination path"
          sx={{
            '& .MuiFilledInput-root': {
              '&::before': {
                display: 'none',
              },
            },
          }}
          InputProps={{
            startAdornment: (
              <InputAdornment position="start">
                <Tooltip title="File explorer" placement="top">
                  <IconButton
                    edge="start"
                    onClick={() => showExplorerDialog('dstPath')}
                    sx={{ p: 0.5 }}
                  >
                    <FolderIcon />
                  </IconButton>
                </Tooltip>
              </InputAdornment>
            ),
          }}
        />
        <Box sx={{ mt: 2 }}>
          <Accordion>
            <AccordionSummary expandIcon={<ExpandMoreIcon />}>
              <Typography>Advanced Options</Typography>
            </AccordionSummary>
            <AccordionDetails>
              <List
                sx={{
                  display: 'flex',
                  flexWrap: 'wrap',
                }}
              >
                {saveOptions.map((saveOption, index) => (
                  <ListItem key={index} sx={{ width: '50%' }}>
                    <ListItemIcon>
                      <Checkbox
                        onChange={() => switchCheckBox(saveOption.state)}
                        checked={options[saveOption.state as keyof typeof options]}
                        noBackground
                      />
                    </ListItemIcon>
                    <ListItemText
                      primary={saveOption.label}
                      secondary={saveOption.label2}
                    />
                  </ListItem>
                ))}
              </List>
            </AccordionDetails>
          </Accordion>
        </Box>

        <FileBrowser
          open={explorerDialogOpen}
          exploreOnlyDirs={true}
          onRequestClose={closeExplorerDialog}
          startDir={explorerParameter === 'srcPath' ? srcPath : dstPath}
        />
      </ActionValidationDialog>

      {openOverwriteDialog ? (
        <ActionValidationDialog
          onAction={saveModel}
          buttonLabel="Overwrite"
          title="Destination Path Already Exists"
          open={openOverwriteDialog}
          onRequestClose={() => setOpenOverwriteDialog(false)}
        >
          <Typography>{`Path "${dstPath}" already exists, do you want to overwrite it?`}</Typography>
        </ActionValidationDialog>
      ) : null}
    </>
  );
};

export default OverwriteModelDialog;

